//@author A0081229H
package storage;

import java.util.ArrayList;

public class Date implements Comparable<Date>, Clonable<Date>, Observable, DateTime{
	private static final int MONTH_FIRST = 1;
	private static final int MONTH_LAST = 12;
	private static final int DAY_FIRST = 1;
	private static final int DAY_LAST = 31;

	private static final String SETYEAR = "setYear";
	private static final String SETMONTH = "setMonth";
	private static final String SETDAY = "setDay";

	private static final String ERROR_MESSAGE_INVALID_MONTH = "Invalid Month";
	private static final String ERROR_MESSAGE_INVALID_DAY = "Invalid Day";

	private Integer year, month, day;
	private ArrayList<Observer> observerList;

	// Constructors

	public Date() {
		try{
			StorageLogger.log(this.getClass().getCanonicalName());
		} catch (Exception e) {

		}
		year = null;
		month = null;
		day = null;
		observerList = new ArrayList<Observer>();
	}

	public Date(Integer pYear) {
		try{
			StorageLogger.log(this.getClass().getCanonicalName());
		} catch (Exception e) {

		}
		year = pYear;
		month = null;
		day = null;
		observerList = new ArrayList<Observer>();
	}

	public Date(Integer pYear, Integer pMonth) throws Exception {
		try{
			StorageLogger.log(this.getClass().getCanonicalName());
		} catch (Exception e) {

		}
		year = pYear;
		if (isMonthValid(pMonth)) {
			month = pMonth;
		} else {
			throw new Exception(ERROR_MESSAGE_INVALID_MONTH);
		}
		day = null;
		observerList = new ArrayList<Observer>();
	}

	public Date(Integer pYear, Integer pMonth, Integer pDay) throws Exception {
		try{
			StorageLogger.log(this.getClass().getCanonicalName());
		} catch (Exception e) {

		}
		observerList = new ArrayList<Observer>();
		year = pYear;
		if (isMonthValid(pMonth)) {
			month = pMonth;
		} else {
			throw new Exception(ERROR_MESSAGE_INVALID_MONTH);
		}
		if (isDayValid(pDay)) {
			day = pDay;
		} else {
			throw new Exception(ERROR_MESSAGE_INVALID_DAY);
		}
		observerList = new ArrayList<Observer>();
	}

	// Access Methods

	public Integer getYear() {
		return year;
	}

	public Integer getMonth() {
		return month;
	}

	public Integer getDay() {
		return day;
	}

	// Verification Methods

	public boolean isEmpty() {
		if (year == null &&
				month == null &&
				day == null) {
			return true;
		} else {
			return false;
		}
	}

	private boolean isMonthValid(Integer pMonth) {
		if (pMonth >= MONTH_FIRST &&
				pMonth <= MONTH_LAST) {
			return true;
		} else {
			return false;
		}
	}

	private boolean isDayValid(Integer pDay) {
		if (pDay >= DAY_FIRST &&
				pDay <= DAY_LAST) {
			return true;
		} else {
			return false;
		}
	}

	// Modification Methods

	public boolean setYear(Integer pYear) {
		try{
			StorageLogger.log(this.getClass().getCanonicalName() +
					Helper.SPACE + SETYEAR + Helper.SPACE + pYear);
		} catch (Exception e) {

		}
		try {
			year = pYear;
			notifyObs();
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	public boolean setMonth(Integer pMonth) throws Exception {
		try{
			StorageLogger.log(this.getClass().getCanonicalName() +
					Helper.SPACE + SETMONTH + Helper.SPACE + pMonth);
		} catch (Exception e) {

		}
		if (isMonthValid(pMonth)) {
			month = pMonth;
			notifyObs();
			return true;
		} else {
			return false;
		}
	}

	public boolean setDay(Integer pDay) throws Exception {
		try{
			StorageLogger.log(this.getClass().getCanonicalName() +
					Helper.SPACE + SETDAY + Helper.SPACE + pDay);
		} catch (Exception e) {

		}
		if (isDayValid(pDay)) {
			day = pDay;
			notifyObs();
			return true;
		} else {
			return false;
		}
	}

	// Interfaces

	@Override
	public int compareTo(Date pDate) {
		return Helper.compareToCustom(getYear(), pDate.getYear(), 
				getMonth(), pDate.getMonth(), 
				getDay(), pDate.getDay());
	}

	@Override
	public Date cloneSelf() throws Exception {
		Date clone;
		if (isEmpty() || getYear() == null) {
			clone = new Date();
		} else if (getMonth() == null) {
			clone = new Date(getYear());
		} else if (getDay() == null) {
			clone = new Date(getYear(), getMonth());
		} else {
			clone = new Date(getYear(), getMonth(), getDay());
		}
		return clone;
	}

	@Override
	public void notifyObs() {
		for (Observer o : observerList) {
			o.update();
		}
	}

	@Override
	public void addObs(Observer o) {
		observerList.add(o);
	}

	@Override
	public Integer getFirstAttribute() {
		return getYear();
	}

	@Override
	public Integer getSecondAttribute() {
		return getMonth();
	}

	@Override
	public Integer getThirdAttribute() {
		return getDay();
	}

	@Override
	public boolean equals(Object obj) {
		if(obj instanceof Date) {
			Date date = (Date) obj;
			return this.month.equals(date.month)
					&& this.day.equals(date.day) && this.year.equals(date.year);
		}
		else {
			return false;
		}
	}
}
